#define _USE_MATH_DEFINES

#include <stdio.h>
#include <png.h>
#include <errno.h>
#include <malloc.h>
#include <windows.h>

struct read_params
{
	FILE* f;
	const unsigned char* data;
	size_t len;
	size_t pos;
};

static void read_file_fn(png_structp png_ptr, png_bytep data, png_size_t length)
{
	png_voidp a = png_get_io_ptr(png_ptr);
	struct read_params* p = (struct read_params*)a;
	size_t was_read = fread(data, 1, length, p->f);
	if (was_read < 0)
	{
		png_error(png_ptr, "cannot read file");
	}
}

static int map_png(const char* fname)
{
	struct read_params params;
	png_structp png_ptr = NULL;
	png_infop info_ptr = NULL;

	int bit_depth, color_type, rowbytes, channels = 1, rc = -1;
	png_uint_32 twidth, theight;
	size_t i, size = 0;
	png_byte* image_data = NULL, *tile_data = NULL, *tile_ptr = NULL;
	const png_byte* row_ptr = NULL;
	png_bytep* row_pointers = NULL;
	png_uint_32 strip_height = 0;
	HANDLE hMapFile = NULL;
	LPCSTR pBuf = NULL;

	printf("loading: %s\n", fname);

	memset(&params, 0, sizeof(struct read_params));
	params.f = fopen(fname, "rb");
	if (params.f == NULL)
		goto out;

	png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
	if (!png_ptr)
		goto out;

	png_set_read_fn(png_ptr, &params, read_file_fn);

	info_ptr = png_create_info_struct(png_ptr);

	if (!info_ptr)
		goto out;

	png_read_info(png_ptr, info_ptr);

	if (png_get_IHDR(png_ptr, info_ptr, &twidth, &theight, &bit_depth, &color_type, NULL, NULL, NULL) != 1)
		goto out;

	if (color_type == PNG_COLOR_TYPE_PALETTE)
	{
		png_set_palette_to_rgb(png_ptr);
		channels = 3;
	}

	if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
	{

		png_set_tRNS_to_alpha(png_ptr);
		channels += 1;
	}

	png_read_update_info(png_ptr, info_ptr);
	rowbytes = png_get_rowbytes(png_ptr, info_ptr);

	size = sizeof(png_byte) * rowbytes * theight;
	image_data = (png_byte*)malloc(size);
	if (image_data == NULL)
		goto out;

	row_pointers = (png_bytep*)malloc(sizeof(png_bytep) * theight);
	if (row_pointers == NULL)
		goto out;

	for (i = 0; i < theight; ++i)
		row_pointers[theight - 1 - i] = image_data + i * rowbytes;

	printf("w,h: %d,%d, rowbytes: %d, size: %lu\n", twidth, theight, rowbytes, size);

	png_read_image(png_ptr, row_pointers);

	hMapFile = CreateFileMapping(
                 INVALID_HANDLE_VALUE,    // use paging file
                 NULL,                    // default security
                 PAGE_READWRITE,           // read/write access
                 0,                       // maximum object size (high-order DWORD)
                 size,                    // maximum object size (low-order DWORD)
                 L"Local\\sprite");      // name of mapping object

	if (hMapFile == NULL)
	{
		fprintf(stderr, "ERROR: CreateFileMapping: %d\n", GetLastError());
		goto out;
	}

	pBuf = (LPCSTR)MapViewOfFile(hMapFile,   // handle to map object
                        FILE_MAP_ALL_ACCESS,     // read/write permission
                        0,
                        0,
                        size);
	
	if (pBuf == NULL)
	{
		fprintf(stderr, "ERROR: MapViewOfFile: %d\n", GetLastError());
		goto out;
	}

	CopyMemory((PVOID)pBuf, image_data, size);
	free(image_data);
	image_data = NULL;
	free(tile_data);
	tile_data = NULL;
	free(row_pointers);
	row_pointers = NULL;

	printf("image mapped into shared memory with name Local\\sprite\n");

	printf("Press ENTER to stop.\n");
	getc(stdin);

	rc = 0;
out:
	if (png_ptr != NULL && info_ptr != NULL)
	{
		png_destroy_read_struct(&png_ptr, &info_ptr, 0);
	}
	else if (png_ptr != NULL)
	{
		png_destroy_read_struct(&png_ptr, 0, 0);
	}

	if (image_data != NULL)
	{
		free(image_data);
	}

	if (tile_data != NULL)
	{
		free(tile_data);
	}

	if (row_pointers != NULL)
	{
		free(row_pointers);
	}

	if (params.f != NULL)
	{
		fclose(params.f);
	}

	return rc;
}


int main()
{
	map_png("sprite.png");
}
